//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include "salloc.h"

STATIC DECL_DATAINIT MemoryRegion   s_staticAllocRegion;
STATIC DECL_DATAINIT virtaddr_t     s_vaCursor;

//
// This function searchs for a most suitable free region for static allocation.
// The region must be in a zone which is <= kernel prefered zone.
// Assume: the free regions was sorted.
// Caller must call InitKernelMapping immediately after call to this function.
//
EXTERN DECL_CODEINIT bool_t InitStaticAlloc(
    const MemoryRegion freeRegions[], uint_t uNumberOfFreeRegions)
{
    assert(0 < uNumberOfFreeRegions);

    uint_t uZoneNo = bsp.uKernelPreferredZoneNo;
    do {
        for (int nFreeRegionNo = uNumberOfFreeRegions - 1;
            0 <= nFreeRegionNo; nFreeRegionNo--) {
            if (freeRegions[nFreeRegionNo].uBasePageNo
                    >= bsp.pMemoryZones[uZoneNo].uBasePageNo
                && freeRegions[nFreeRegionNo].uLimitPageNo
                    <= bsp.pMemoryZones[uZoneNo].uLimitPageNo) {

                s_staticAllocRegion.uBasePageNo  =
                    freeRegions[nFreeRegionNo].uBasePageNo;
                s_staticAllocRegion.uLimitPageNo =
                    freeRegions[nFreeRegionNo].uLimitPageNo;

                assert(IS_VALID_PAGENO(s_staticAllocRegion.uLimitPageNo - 1));

                s_vaCursor =
                    PAGENO_TO_VIRTADDR(s_staticAllocRegion.uLimitPageNo);

                return TRUE;
            }
        }
    } while (0 < uZoneNo--);

    kputs("*KERNEL ERROR* No suitable free regions for static allocation.\n");
    return FALSE;
}

//
// Disable StaticAlloc and remove allocated region from free pages.
// Assume that freeRegions was sorted till the region of StaticAlloc exploits.
// UninitStaticAlloc() will substract allocated memory from the free region.
//
EXTERN DECL_CODEINIT void UninitStaticAlloc(
    MemoryRegion freeRegions[], uint_t uNumberOfFreeRegions)
{
    for (uint_t i = 0; i < uNumberOfFreeRegions; i++) {
        if (s_staticAllocRegion.uBasePageNo == freeRegions[i].uBasePageNo) {
            assert(s_staticAllocRegion.uLimitPageNo
                    == freeRegions[i].uLimitPageNo);
            assert(s_vaCursor
                    >= PAGENO_TO_VIRTADDR(s_staticAllocRegion.uBasePageNo));

            freeRegions[i].uLimitPageNo = VIRTADDR_TO_PAGENO(s_vaCursor);

            s_staticAllocRegion.uBasePageNo  = 0;
            s_staticAllocRegion.uLimitPageNo = 0;
            return;
        }
    }

    assert0msg("Failed to find the region of static allocation\n");
}

//
// Start the static allocation region from kernel prefered zone.
// If memory full return 0, else return the address.
// Assume: the alignment must be 2 order.
//
EXTERN DECL_CODEINIT virtaddr_t StaticAlloc(size_t size, size_t uAlignment)
{
    // assert initialized
    assert(s_staticAllocRegion.uBasePageNo < s_staticAllocRegion.uLimitPageNo);

    s_vaCursor = (virtaddr_t)RoundDown2((size_t)s_vaCursor - size, uAlignment);

    if (s_vaCursor < PAGENO_TO_VIRTADDR(s_staticAllocRegion.uBasePageNo)) {
        kputs("*KERNEL ERROR* No enough memory in static allocation region\n");
        return 0;
    }

    return s_vaCursor;
}

EXTERN DECL_CODEINIT PageTabEntry * CDECL AllocKernelPageTable()
{
    return (PageTabEntry *)StaticAlloc(c_uSizeOfPageTab, c_uSizeOfPageTab);
}

EXTERN DECL_CODEINIT void DumpStaticAlloc(const char *szTitle)
{
#ifndef _RELEASE
    kprintf("%s: Static allocation region [%x, %x), cursor: %x\n", szTitle,
            s_staticAllocRegion.uBasePageNo, s_staticAllocRegion.uLimitPageNo,
            PHYSADDR(s_vaCursor));
#endif
}
